#include <stdint.h>
#include <stdio.h>
#include <limits.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <string.h>
#include <pthread.h>
#include "global.h"
#include "ietDiLib.h"
#include "prt_log.h"

#define LIB_VERSION(a,b,c)                  ((a << 24) + (b << 16) + (c))

#define DI_MAX_CHANNEL      32              /* DI 最大通道数 */
#if (HARD_VER == 0)
#define DI_DIM_CHANNEL      16              /* DI 在 DI 模块中通道数 */
#define DI_SOEM_CHANNEL     12              /* DI 在 SOE 模块中通道数 */
#define DI_ACM_CHANNEL      8               /* DI 在 AC 模块中通道数 */
#elif (HARD_VER == 1)
#define DI_DIM_CHANNEL      24              /* DI 在 DI 模块中通道数 */
#define DI_SOEM_CHANNEL     12              /* DI 在 SOE 模块中通道数 */
#define DI_ACM_CHANNEL      8               /* DI 在 AC 模块中通道数 */
#endif

#define DI_DRV_NAME         "pcie_di"       /* DI 驱动名称 */

/* IOCTL 命令定义 */
#define IOCTL_DI_MAGIC                      'P'                                         /* 标识设备驱动 */
#define IOCTL_DI_SET_MODE                   _IOW(IOCTL_DI_MAGIC, 1, uint8_t)            /* 配置 DI 模式 */
#define IOCTL_DI_SET_EDGE                   _IOW(IOCTL_DI_MAGIC, 2, uint8_t)            /* 配置 DI 边沿 */
#define IOCTL_DI_SET_FILTER                 _IOW(IOCTL_DI_MAGIC, 3, uint16_t)           /* 配置 DI 滤波时间 */
#define IOCTL_DI_GET_LEVEL                  _IOR(IOCTL_DI_MAGIC, 4, uint8_t)            /* 获取 DI 电平 */
#define IOCTL_DI_WAIT_IRQ                   _IOR(IOCTL_DI_MAGIC, 5, uint8_t)            /* 等待 DI 中断 */

#define IOCTL_DI_GET_DRV_VERSION            _IOR(IOCTL_DI_MAGIC, 101, uint32_t)         /* 获取驱动版本号 */
#define IOCTL_DI_GET_FPGA_VERSION           _IOR(IOCTL_DI_MAGIC, 102, uint32_t)         /* 获取 FPGA 版本号 */


/* 应用层 */
enum DiMode {
    DI_MOD_LEVEL            = 0,            /* 电平状态检测 */
    DI_MOD_IRQ              = 1,            /* 中断模式 */
};
enum DiEdge {
    DI_EDGE_FALL            = 0,            /* 下降沿 */
    DI_EDGE_RISE            = 1,            /* 上升沿 */
    DI_EDGE_RISE_FALL       = 2,            /* 上升和下降沿 */
};

/* 驱动层*/
enum DiDevMode {
    DI_DEV_MOD_LEVEL        = 0,            /* 电平状态检测 */
    DI_DEV_MOD_IRQ          = 3,            /* 中断模式 */
};
enum DiDevEdge {
    /* 硬件为反逻辑，须软件反转边沿寄存器电平 */
    DI_DEV_EDGE_RISE        = 1,            /* 上升沿 */
    DI_DEV_EDGE_FALL        = 0,            /* 下降沿 */
    DI_DEV_EDGE_RISE_FALL   = 2,            /* 上升和下降沿 */
};

struct ThreadCtrl_t {
    char name[64];                          /* 线程名 */
    pthread_t id;                           /* 线程标识 */
};

struct DiDev_t {
    void *parent;                                           /* 父指针 */
    char name[32];                                          /* 设备名 */
    uint8_t index;                                          /* 设备号 */

    uint8_t setMode;                                        /* 模式 */
    uint8_t setEdge;                                        /* 边沿 */
    uint16_t setFilter;                                     /* 滤波时间, us */
    uint8_t getLevel;                                       /* 电平 */
    uint8_t getEdge;                                        /* 电平 */
    void (*irqCallback)(int channel, int edge, int level);  /* 回调函数 */

    int fd;                                                 /* 文件 */
    char path[PATH_MAX];                                    /* 路径 */
    struct ThreadCtrl_t recvThread;                         /* 接收线程 */
};
struct DiCtrl_t {
    char name[16];                                          /* 控制器名 */
    uint8_t num;                                            /* 设备数量 */
    uint8_t max_num;                                        /* 最大数量 */
    struct DiDev_t dev[DI_MAX_CHANNEL];                     /* 设备描述 */
    uint32_t drvVer;                                        /* 驱动 版本号 */
    uint32_t fpgaVer;                                       /* FPGA 版本号 */
    uint32_t libVer;                                        /* 动态库 版本号 */

    uint8_t type;                                           /* 模块类型 */
};
/* DI 控制器 */
static struct DiCtrl_t DiCtrl;

/* 获取设备数量 */
static uint8_t DIMGetDevNum(int type)
{
    uint8_t num = 0;

    switch (type) {
    case IET_ECU:
        goto err_retrun;
    case IET_DI:
        num = DI_DIM_CHANNEL;
        break;
    case IET_DO:
    case IET_AI:
    case IET_AO:
    case IET_PI:
        goto err_retrun;
    case IET_SOE:
        num = DI_SOEM_CHANNEL;
        break;
    case IET_AC:
        num = DI_ACM_CHANNEL;
        break;
    case IET_PT100:
    case IET_IEPE:
        goto err_retrun;
    default:
        prt_erro("Invalid type %d for %s\n", type, __func__);
        return 0;
    }
    prt_info("%s get %d device\n", __func__, num);

    return num;

err_retrun:
    prt_erro("%s get no device\n", __func__);

    return 0;
};

/* 接收线程 */
static void *DIMRecvThreadFunc(void *arg)
{
    struct DiDev_t *pdev = (struct DiDev_t *)arg;
    int ret = 0;
    int edge = 0;

    /* 检查参数 */
    if (!pdev || (pdev->fd == 0)) {
        prt_erro("Invalid parameters for %s\n", __func__);
        return NULL;
    }

    prt_dbug("Start %s %s\n", pdev->name, __func__);
    while (1) {
        /* 获取 DI 数值 */
        ret = ioctl(pdev->fd, IOCTL_DI_WAIT_IRQ, &pdev->getLevel);
        if (ret < 0) {
            prt_erro("Wait DI%d irq failed\n", pdev->index);
            return NULL;
        }
        if (pdev->getLevel == 1) {
            pdev->getEdge = DI_DEV_EDGE_RISE;
            edge = DI_EDGE_RISE;
        } else {
            pdev->getEdge = DI_DEV_EDGE_FALL;
            edge = DI_EDGE_FALL;
        }
        if (pdev->irqCallback != NULL) {
            pdev->irqCallback(pdev->index, edge, pdev->getLevel);
            prtt_info("Trigger DI%d edge %d value %d\n", pdev->index, pdev->getEdge, pdev->getLevel);
        } else {
            prt_erro("Trigger DI%d edge %d value %d, but no callback func\n", 
                    pdev->index, pdev->getEdge, pdev->getLevel);
        }
    }
    prt_dbug("Return %s %s\n", pdev->name, __func__);

    return NULL;
}

/* 打开设备 */
int DIMOpen(int moduleType)
{
    struct DiCtrl_t *pctrl = &DiCtrl;
    struct DiDev_t *pdev = &pctrl->dev[0];
    int ret = 0;
    uint8_t i = 0;
    int8_t j = 0;

    pctrl->num = DIMGetDevNum(moduleType);
    pctrl->max_num = DI_MAX_CHANNEL;
    pctrl->type = (uint8_t)moduleType;
    /* 检查参数 */
    if (pctrl->num > pctrl->max_num) {
        prt_erro("Get DI number %d, max is %d\n", pctrl->num, pctrl->max_num);
        return -EFAULT;
    }
    /* 生成路径 */
    snprintf(pctrl->name, sizeof(pctrl->name), "%s", DI_DRV_NAME);
    for (i = 0; i < pctrl->num; i++) {
        pdev = &pctrl->dev[i];
        pdev->parent = pctrl;
        snprintf(pdev->name, sizeof(pdev->name), "%s%d", pctrl->name, i);
        pdev->index = i;
        pdev->fd = -ENODEV;
        ret = snprintf(pdev->path, PATH_MAX, "/dev/%s", pdev->name);
        if (ret < 0 || ret >= PATH_MAX) {
            prt_erro("Generate %s path failed\n", pdev->name);
            return -ENAMETOOLONG;
        }
        /* 打开设备 */
        pdev->fd = open(pdev->path, O_RDWR);
        if (pdev->fd < 0) {
            ret = errno;
            prt_erro("Open %s device failed, %s\n", pdev->path, strerror(ret));
            ret = -ret;
            goto err_close_fd;
        }
        prt_dbug("Open %s device success\n", pdev->path);
    }

    pdev = &pctrl->dev[0];
    /* 获取驱动版本号 */
    ret = ioctl(pdev->fd, IOCTL_DI_GET_DRV_VERSION, &pctrl->drvVer);
    if (ret < 0) {
        prt_erro("Get DI driver version 0x%08x failed\n", pctrl->drvVer);
        goto err_close_fd;
    }
    prt_dbug("Get DI driver version 0x%08x\n", pctrl->drvVer);
    /* 获取 FPGA 版本号 */
    ret = ioctl(pdev->fd, IOCTL_DI_GET_FPGA_VERSION, &pctrl->fpgaVer);
    if (ret < 0) {
        prt_erro("Get DI FPGA version 0x%08x failed\n", pctrl->fpgaVer);
        goto err_close_fd;
    }
    prt_dbug("Get DI FPGA version 0x%08x\n", pctrl->fpgaVer);
    pctrl->libVer = LIB_VERSION(DI_VER_MAJOR, DI_VER_MINOR, DI_VER_PATCH);
    prt_dbug("Get DI library version 0x%08x\n", pctrl->libVer);

    return 0;

err_close_fd:
    for (j = i - 1; j >= 0; j--) {
        pdev = &pctrl->dev[j];
        close(pdev->fd);
        pdev->fd = -ENODEV;
    }

    return ret;
}

/* 初始化设备 */
int DIMInit(int channel, struct DimParam dimParam)
{
    struct DiCtrl_t *pctrl = &DiCtrl;
    struct DiDev_t *pdev = &pctrl->dev[0];
    struct ThreadCtrl_t *pthread = &pdev->recvThread;
    int ret = 0;
    uint8_t i = 0;
    uint8_t start = 0;
    uint8_t end = 0;
    int8_t j = 0;
    uint8_t mode = 0;
    uint8_t edge = 0;

    /* 检查参数 */
    if (channel < 0 || 
        dimParam.window < 0 || dimParam.window > UINT16_MAX) {
        prt_erro("Invalid parameters for %s\n", __func__);
        return -EFAULT;
    }
    if (channel >= pctrl->num) {
        start = 0;
        end = pctrl->num;
    } else {
        start = channel;
        end = channel + 1;
    }
    /* 配置模式 */
    switch (dimParam.mode) {
    case DI_MOD_LEVEL:
        mode = DI_DEV_MOD_LEVEL;
        break;
    case DI_MOD_IRQ:
        mode = DI_DEV_MOD_IRQ;
        break;
    default:
        prt_erro("Invalid mode %d for %s\n", dimParam.mode, __func__);
        ret = -EFAULT;
        goto err_return;
    }
    /* 配置边沿 */
    switch (dimParam.type) {
    case DI_EDGE_FALL:
        edge = DI_DEV_EDGE_FALL;
        break;
    case DI_EDGE_RISE:
        edge = DI_DEV_EDGE_RISE;
        break;
    case DI_EDGE_RISE_FALL:
        edge = DI_DEV_EDGE_RISE_FALL;
        break;
    default:
        prt_erro("Invalid edge %d for %s\n", dimParam.type, __func__);
        ret = -EFAULT;
        goto err_return;
    }
    for (i = start; i < end; i++) {
        pdev = &pctrl->dev[i];
        pdev->setMode = mode;
        pdev->setEdge = edge;
        pdev->setFilter = dimParam.window;
        /* 检查参数 */
        if (pdev->fd == 0) {
            prt_erro("Invalid %s->fd for %s\n", pdev->name, __func__);
            ret = -EFAULT;
            goto err_return;
        }
        /* 配置 DI 模式 */
        ret = ioctl(pdev->fd, IOCTL_DI_SET_MODE, &pdev->setMode);
        if (ret < 0) {
            prt_erro("Set %s setMode %d failed\n", pdev->name, pdev->setMode);
            goto err_return;
        }
        prt_dbug("Set %s setMode %d success\n", pdev->name, pdev->setMode);
        /* 配置 DI 边沿 */
        ret = ioctl(pdev->fd, IOCTL_DI_SET_EDGE, &pdev->setEdge);
        if (ret < 0) {
            prt_erro("Set %s setEdge %d failed\n", pdev->name, pdev->setEdge);
            goto err_return;
        }
        prt_dbug("Set %s setEdge %d success\n", pdev->name, pdev->setEdge);
        /* 配置 DI 滤波 */
        ret = ioctl(pdev->fd, IOCTL_DI_SET_FILTER, &pdev->setFilter);
        if (ret < 0) {
            prt_erro("Set %s filter %dus failed\n", pdev->name, pdev->setFilter);
            goto err_return;
        }
        prt_dbug("Set %s filter %dus success\n", pdev->name, pdev->setFilter);
        if (pdev->setMode == DI_DEV_MOD_IRQ) {
            /* 创建线程 */
            snprintf(pthread->name, sizeof(pthread->name), "%s%s", "recv_", pdev->name);
            ret = pthread_create(&pthread->id, NULL, DIMRecvThreadFunc, pdev);
            if (ret != 0) {
                prt_erro("Create %s recv thread failed\n", pdev->name);
                ret = -ret;
                goto err_cancel_thread;
            } else {
                prt_dbug("Create %s recv thread success\n", pdev->name);
            }
        } else {
            /* 取消线程 */
            if (pthread->id != 0) {
                pthread_cancel(pthread->id);
                pthread->id = 0;
            }
        }
    }

    return 0;

err_cancel_thread:
    for (j = i - 1; j >= 0; j--) {
        pdev = &pctrl->dev[j];
        pthread_cancel(pthread->id);
        pthread->id = 0;
    }
err_return:

    return ret;
}

int DIMRead(int channel, int *data)
{
    struct DiCtrl_t *pctrl = &DiCtrl;
    struct DiDev_t *pdev = &pctrl->dev[0];
    int ret = 0;

    /* 检查参数 */
    if (!data || 
        channel < 0 || channel >= pctrl->num) {
        prt_erro("Invalid parameters for %s\n", __func__);
        return -EFAULT;
    }
    pdev = &pctrl->dev[channel];
    /* 检查参数 */
    if (pdev->fd == 0) {
        prt_erro("Invalid %s->fd for %s\n", pdev->name, __func__);
        return -EFAULT;
    }
    /* 获取 DI 数值 */
    ret = ioctl(pdev->fd, IOCTL_DI_GET_LEVEL, &pdev->getLevel);
    if (ret < 0) {
        prt_erro("Get DI%d level failed\n", pdev->index);
        return -EFAULT;
    }
    prt_dbug("Get DI%d value %d\n", pdev->index, pdev->getLevel);
    *data = pdev->getLevel;

    return 0;
}

int DIMReadAll(int *data)
{
    struct DiCtrl_t *pctrl = &DiCtrl;
    struct DiDev_t *pdev = &pctrl->dev[0];
    int ret = 0;
    uint8_t i = 0;

    /* 检查参数 */
    if (!data) {
        prt_erro("Invalid parameters for %s\n", __func__);
        return -EFAULT;
    }
    for (i = 0; i < pctrl->num; i++) {
        pdev = &pctrl->dev[i];
        /* 检查参数 */
        if (pdev->fd == 0) {
            prt_erro("Invalid %s->fd for %s\n", pdev->name, __func__);
            return -EFAULT;
        }
        /* 获取 DI 数值 */
        ret = ioctl(pdev->fd, IOCTL_DI_GET_LEVEL, &pdev->getLevel);
        if (ret < 0) {
            prt_erro("Get DI%d level failed\n", pdev->index);
            return -EFAULT;
        }
        prt_dbug("Get DI%d value %d\n", pdev->index, pdev->getLevel);
        data[i] = pdev->getLevel;
    }

    return 0;
}

int DIMClose()
{
    struct DiCtrl_t *pctrl = &DiCtrl;
    struct DiDev_t *pdev = &pctrl->dev[0];
    struct ThreadCtrl_t *pthread = &pdev->recvThread;
    int ret = 0;
    uint8_t i = 0;

    for (i = 0; i < pctrl->num; i++) {
        pdev = &pctrl->dev[i];
        /* 取消线程 */
        if (pthread->id != 0) {
            pthread_cancel(pthread->id);
            pthread->id = 0;
        }
        /* 检查参数 */
        if (pdev->fd == 0) {
            prt_erro("Invalid %s->fd for %s\n", pdev->name, __func__);
            return -EFAULT;
        }
        /* 关闭设备 */
        ret = close(pdev->fd);
        if (ret != 0) {
            prt_erro("Close %s failed\n", pdev->path);
            return -EFAULT;
        }
        pdev->fd = 0;
        prt_dbug("Close %s success\n", pdev->path);
    }

    return 0;
}

/* 注册回调函数 */
void registerCallback(int channel, CallbackFunction callback)
{
    struct DiCtrl_t *pctrl = &DiCtrl;
    struct DiDev_t *pdev = &pctrl->dev[0];
    uint8_t i = 0;
    uint8_t start = 0;
    uint8_t end = 0;

    /* 检查参数 */
    if (channel < 0 || !callback) {
        prt_erro("Invalid parameters for %s\n", __func__);
        return;
    }
    if (channel >= pctrl->num) {
        start = 0;
        end = pctrl->num;
    } else {
        start = channel;
        end = channel + 1;
    }
    /* 注册回调 */
    for (i = start; i < end; i++) {
        pdev = &pctrl->dev[i];
        pdev->irqCallback = callback;
    }
}

/* 获取驱动版本号 */
int DIMGetDrvVer()
{
    struct DiCtrl_t *pctrl = &DiCtrl;

    prt_dbug("%s %d.%d.%d\n", __func__, ((pctrl->drvVer >> 24) & 0xFF), 
             ((pctrl->drvVer >> 16) & 0xFF), (pctrl->drvVer & 0xFFFF));

    return (int)(pctrl->drvVer >> 16);
}

/* 获取 FPGA 版本号 */
int DIMGetFpgaVer()
{
    struct DiCtrl_t *pctrl = &DiCtrl;

    prt_dbug("%s %d.%d\n", __func__, ((pctrl->fpgaVer >> 8) & 0xFF), 
             (pctrl->fpgaVer & 0xFF));

    return (int)(pctrl->fpgaVer);
}

/* 获取动态库版本号 */
int DIMGetLibVer()
{
    struct DiCtrl_t *pctrl = &DiCtrl;

    prt_dbug("%s %d.%d.%d\n", __func__, ((pctrl->libVer >> 24) & 0xFF), 
             ((pctrl->libVer >> 16) & 0xFF), (pctrl->libVer & 0xFFFF));

    return (int)(pctrl->libVer >> 16);
}
